/*
	In this source file to watershed.h (see there for more info), the implementation of different gradients are given here.
	These gradients should be performed on an image before calling watershed.
	Arguments are explained in watershed.h
*/

#include "watershed.h"

using namespace pip;
using namespace std;

// what gradient should be applied?
// give the input image, an otp(tion) to chose gradient and, if necessary, parameter...
// the second parameter is only used if scale-space usage is desired.
int applygradient(Image<short> &in, unsigned int opt, int param,int param2){
	if(opt > 14 || (opt <= 9 && opt > 4) )
		return 1;     // wrong user input
	else if(opt==0)
		return 0;     // perform no gradient
	else if(opt==1)
		apply_grad_e(in,param); // perform dilated grad on input image
	else if(opt==2)
		apply_grad(in,param);   // normal grad
	else if(opt==3)
		apply_canny(in,param); // canny-edge
	else if(opt==4)
		apply_mg(in,param);   // multi-scale-gradient
	else if(opt>9){
		Image<float> tmp,inputf;
		convertAllToFloat(in,inputf);
		gauss(inputf, tmp, float(param2)/10, 0, 0, 5); //blur before ...
		convertAllToShort(tmp,in);
		applygradient(in, opt%10,param); // recursivly calling applygradient ... and change opt(tion)
	}
	return 0;
}

// the typical morphological grad: grad(I) = dilate(I,se) - erosion(I,se)
inline void apply_grad(Image<short> &in,int s){  // the function is slightly too big for inline ... the compiler decides
	Image<float> se,dil,ero,grad,inputf;
	grad.resize(in.dimension());
	convertAllToFloat(in,inputf);
	discStructureElement(se,s);          // SE for morphological operations
	grayDilate(inputf, dil,se);
	grayErode(inputf, ero,se);
	for(int x=0;x<in.dim(0);++x){
		for(int y=0;y<in.dim(1);++y){
			grad(x,y)=(dil(x,y)-ero(x,y)); // add them
		}
	}
	convertAllToShort(grad,in);         //compute output
}

// the eroded grad - fills the wholes in the gradient a littlebit better...
void apply_grad_e(Image<short> &in, int s){ // same as above, with additional erosion
	apply_grad(in,s);			// reuse function - even though its a littlebit slower than giving explicit code...?
	Image<short> se,ero;
	discStructureElement(se,s);       // SE for morphological operations
	grayErode(in, ero,se);            // once more erosion . . .
	in=ero;
}

// multi-scale gradient: a gradient averaged over 'grad's with different sized Structure Elements...
// more robust to local extrema and stepedges
void apply_mg(Image<short> &in, int s){ 
	Image<float> se,se2,dil,ero,grad,mg,inputf;
	grad.resize(in.dimension());
	mg.resize(in.dimension());
	mg.fill(0);
	convertAllToFloat(in,inputf);
	for(int s_=1; s_<s;++s_){       // apply for different sizes 's'...
		discStructureElement(se,(s_+1)); // structureing elements...
		discStructureElement(se2,s_);
		grayDilate(inputf, dil,se); //erode and dilate ...
		grayErode(inputf, ero,se);
		for(int x=0;x<in.dim(0);++x){
			for(int y=0;y<in.dim(1);++y){
				grad(x,y)=dil(x,y)-ero(x,y); // find the grad...
			}
		}
		grayErode(grad, ero,se2);
		for(int x=0;x<in.dim(0);++x){
			for(int y=0;y<in.dim(1);++y){
				mg(x,y)=mg(x,y)+ero(x,y); // and sum the different results
			}
		}
	}
	for(int x=0;x<in.dim(0);++x){
		for(int y=0;y<in.dim(1);++y){
			mg(x,y)/=s; // and sum the different results
		}
	}
	convertAllToShort(mg,in);  // averaging is done by the conversion from float ...
}
		
// well . . . the canny edge detector.
void apply_canny(Image<short> &in, int s){
	Image<float> outputf,tmp,se;
	cannyEdgeDetector(in,outputf,float(s%10));
	if(s > 10){
		discStructureElement(se,4);
		grayDilate(outputf, tmp,se);
		convertAllToShort(tmp,in);
		return;
	}
	convertAllToShort(outputf,in);
}
	

